package sk.tuke.magsa.tools.generator;

import java.io.PrintWriter;
import sk.tuke.magsa.tools.metamodel.Entity;
import sk.tuke.magsa.tools.metamodel.Model;
import sk.tuke.magsa.tools.metamodel.Property;
import sk.tuke.magsa.tools.metamodel.Reference;
import sk.tuke.magsa.tools.metamodel.Type;
import sk.tuke.magsa.tools.metamodel.constraints.Constraint;
import sk.tuke.magsa.tools.metamodel.constraints.Length;
import sk.tuke.magsa.tools.metamodel.constraints.Range;
import sk.tuke.magsa.tools.metamodel.constraints.Required;

//SEE ALSO: Transformer Generation
//http://martinfowler.com/dslwip/TransformerGeneration.html
public class DatabaseScriptGenerator extends Generator {
    public DatabaseScriptGenerator(Model model) {
        super(model);
    }

    @Override
    public void generate() throws GeneratorException {
        PrintWriter out = createWriter(getResourcesDestinationDir() + "/database.sql");
        Reference[] ref;
        String fromRef = null;
        String toRef = null;
        
        for (Entity entity : getModel().getEntities()) {
            //number of properties in entity because of writing commas in database.sql
            int numOfProperties = entity.getProperties().length;
            String builder = "";
            out.printf("CREATE TABLE %s (\n", entity.getName());
            out.printf("  ident INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY,");
            
             ref = entity.getOutgoingReferences();
             if(ref.length > 0)
             {
                 fromRef = ref[0].getFrom().getName();
                 toRef = ref[0].getTo().getName();
                 
                 out.printf("\nident_%s INTEGER,", toRef);
             }
            
            for (Property property: entity.getProperties())
            {
                builder += (getLine(property.getName(), property, property.getConstraints()));
                  
                if(--numOfProperties != 0)
                    builder += (",");
            }
            
            builder += ("\n);\n\n");
            out.printf(builder);
            
            if(ref.length > 0)
            out.printf("ALTER TABLE %s ADD CONSTRAINT %s_fkey FOREIGN KEY (ident_%s) REFERENCES %s(ident);", fromRef, toRef, toRef, toRef);
        }
        out.close();
    }
    
    private String getSqlType(Type type, Length ln)
    {
        switch(type)
        {
            case INTEGER: return "Integer";
            case STRING:
            {
                if(ln == null)
                    return "Varchar(255)";
                else return "Varchar(" + ln.getMaxLength() + ")";
            }
            case REAL: return "Float(6)";
            
            default: return "Varchar(255)";                
        }
    }

    private String getLine(String name, Property property, Constraint[] constraints) {
        String str;
        String sqlType = null;
        String req = null;
        String range = null;
        str = ("\n" + name + " ");
        
        if(constraints != null)
        {
            for(Constraint con : constraints)
            {
//                Method m = null;
//                try{
//                 m = con.getClass().getMethod("supportsType", sk.tuke.magsa.tools.metamodel.Type.class);
//                }
//                catch(NoSuchMethodException e)
//                {
//                    System.out.println(e.getMessage());
//                }
//                Object o = m.invoke(con, Type.INTEGER);
                if(con.getClass().equals(Length.class))
                {
                    sqlType = (getSqlType(property.getType(), (Length)con));
                }
                else if(con.getClass().equals(Required.class))
                {
                    req = "NOT NULL";
                }
                else if(con.getClass().equals(Range.class) && ((Range)con).supportsType(property.getType()))
                {
                    range = getSqlCheckRange(property, (Range)con);
                }
            }
        }
        if(sqlType == null)
            sqlType = getSqlType(property.getType(), null);
        str += (sqlType);
        
        if(req != null)
            str += (" " + req);
        
        if(range != null)
            str += (" " + range);
        
        
        return str;
    }

    private String getSqlCheckRange(Property prop, Range con) {
        return "CHECK (" + prop.getName() + " > " + con.getMinValue() + " AND " + prop.getName() + " < " + con.getMaxValue() + ")";
    }


}
